﻿#include "RIFFReader.h"
#include "lang/Exception.h"
#include "lang/Thread.h"
#include "lang/String.h"
#include "lang/Math.h"
using namespace yzrilyzr_array;
using namespace yzrilyzr_io;
using namespace yzrilyzr_lang;

namespace yzrilyzr_util{
	RIFFReader::RIFFReader(InputStream * stream){
		if(dynamic_cast<RIFFReader *>(stream)){
			root=((RIFFReader *)stream)->root;
		} else{
			root=this;
		}
		this->stream=stream;
		// Check for RIFF nullptr paddings,
		int32_t b;
		while(true){
			b=read();
			if(b == -1){
				fourcc=""; // don't put nullptr value into fourcc,
				// because it is expected to
				// always contain a string value
				riff_type="";
				ckSize=0;
				avail=0;
				return;
			}
			if(b != 0){
				break;
			}
		}
		ByteArray fourcc(4);
		fourcc[0]=(int8_t)b;
		readFully(fourcc, 1, 3);
		this->fourcc=String(fourcc);
		ckSize=readUnsignedInt();
		avail=ckSize;
		if(getFormat() == "RIFF" || getFormat() == "LIST"){
			ByteArray format(4);
			readFully(format);
			this->riff_type=String(format);
		}
	}
	int32_t RIFFReader::read(){
		if(avail == 0){
			return -1;
		}
		int32_t b=stream->read();
		if(b == -1){
			avail=0;
			return -1;
		}
		avail--;
		filepointer++;
		return b;
	}
	u_index RIFFReader::read(ByteArray & b, u_index offset, u_index len){
		if(avail == 0){
			return -1;
		}
		if(len > avail){
			u_index rlen=stream->read(b, offset, (u_index)avail);
			if(rlen != -1)
				filepointer+=rlen;
			avail=0;
			return rlen;
		} else{
			u_index ret=stream->read(b, offset, len);
			if(ret == -1){
				avail=0;
				return -1;
			}
			avail-=ret;
			filepointer+=ret;
			return ret;
		}
	}
	u_index RIFFReader::skip(u_index n){
		if(n <= 0 || avail == 0){
			return 0;
		}
		// will not skip more than
		u_index remaining=Math::min(n, avail);
		while(remaining > 0){
			// Some input streams like FileInputStream can return more bytes,
			// when EOF is reached.
			u_index ret=Math::min(stream->skip(remaining), remaining);
			if(ret == 0){
				// EOF or not? we need to check.
				Thread::yield();
				if(stream->read() == -1){
					avail=0;
					break;
				}
				ret=1;
			} else if(ret < 0){
				// the skip should not return negative value, but check it also
				avail=0;
				break;
			}
			remaining-=ret;
			avail-=ret;
			filepointer+=ret;
		}
		return n - remaining;
	}
	u_index RIFFReader::available(){
		return avail > 0x7fffffff?0x7fffffff:(u_index)avail;
	}
	void RIFFReader::close(){
		finish();
		stream->close();
	}
	void RIFFReader::finish(){
		if(avail != 0){
			skip(avail);
		}
	}
	void RIFFReader::readFully(ByteArray & b, u_index off, u_index len){
		if(len < 0)
			throw Exception();
		while(len > 0){
			u_index s=read(b, off, len);
			if(s < 0)
				throw new Exception("EOF");
			if(s == 0)
				Thread::yield();
			off+=s;
			len-=s;
		}
	}
	// Read 32 bit unsigned integer from stream
	uint32_t RIFFReader::readUnsignedInt(){
		int64_t ch1=read();
		int64_t ch2=read();
		int64_t ch3=read();
		int64_t ch4=read();
		if(ch1 < 0)
			throw Exception("EOF");
		if(ch2 < 0)
			throw Exception("EOF");
		if(ch3 < 0)
			throw Exception("EOF");
		if(ch4 < 0)
			throw Exception("EOF");
		return ch1 + (ch2 << 8) | (ch3 << 16) | (ch4 << 24);
	}
	String RIFFReader::getFormat(){
		return fourcc;
	}
	void RIFFReader::readFully(ByteArray & b){
		readFully(b, 0, b.length);
	}
	int64_t RIFFReader::getFilePointer(){
		return root->filepointer;
	}
	bool RIFFReader::hasNextChunk(){
		if(lastiterator != nullptr)
			lastiterator->finish();
		return avail != 0;
	}
	std::shared_ptr<RIFFReader> RIFFReader::innerChunk(){
		if(avail > 0)return std::make_shared<RIFFReader>(this);
		return nullptr;
	}
	std::shared_ptr<RIFFReader> RIFFReader::nextChunk(){
		if(lastiterator != nullptr)
			lastiterator->finish();
		if(avail == 0)
			return nullptr;
		lastiterator=std::make_shared<RIFFReader>(this);
		return lastiterator;
	}
	String RIFFReader::getType(){
		return riff_type;
	}
	u_index RIFFReader::getSize(){
		return ckSize;
	}
	// Read ASCII chars from stream
	String RIFFReader::readString(u_index len){
		ByteArray buff(len);
		readFully(buff);
		/*for(u_index i=0;i < buff.length;i++){
			if(buff[i] == 0){
				return "";
			}
		}*/
		return String(buff);
	}
	// Read 8 bit signed integer from stream
	int8_t RIFFReader::readByte(){
		int32_t ch=read();
		if(ch < 0)
			throw Exception("EOF");
		return (int8_t)ch;
	}
	// Read 16 bit signed integer from stream
	int16_t RIFFReader::readShort(){
		int32_t ch1=read();
		int32_t ch2=read();
		if(ch1 < 0)
			throw Exception("EOF");
		if(ch2 < 0)
			throw Exception("EOF");
		return (short)(ch1 | (ch2 << 8));
	}
	// Read 32 bit signed integer from stream
	int32_t RIFFReader::readInt(){
		int32_t ch1=read();
		int32_t ch2=read();
		int32_t ch3=read();
		int32_t ch4=read();
		if(ch1 < 0)
			throw Exception("EOF");
		if(ch2 < 0)
			throw Exception("EOF");
		if(ch3 < 0)
			throw Exception("EOF");
		if(ch4 < 0)
			throw Exception("EOF");
		return ch1 | (ch2 << 8) | (ch3 << 16) | (ch4 << 24);
	}
	// Read 64 bit signed integer from stream
	int64_t RIFFReader::readLong(){
		int64_t ch1=read();
		int64_t ch2=read();
		int64_t ch3=read();
		int64_t ch4=read();
		int64_t ch5=read();
		int64_t ch6=read();
		int64_t ch7=read();
		int64_t ch8=read();
		if(ch1 < 0)
			throw Exception("EOF");
		if(ch2 < 0)
			throw Exception("EOF");
		if(ch3 < 0)
			throw Exception("EOF");
		if(ch4 < 0)
			throw Exception("EOF");
		if(ch5 < 0)
			throw Exception("EOF");
		if(ch6 < 0)
			throw Exception("EOF");
		if(ch7 < 0)
			throw Exception("EOF");
		if(ch8 < 0)
			throw Exception("EOF");
		return ch1 | (ch2 << 8) | (ch3 << 16) | (ch4 << 24) | (ch5 << 32) | (ch6 << 40) | (ch7 << 48) | (ch8 << 56);
	}
	// Read 8 bit unsigned integer from stream
	uint8_t RIFFReader::readUnsignedByte(){
		int32_t ch=read();
		if(ch < 0)
			throw Exception("EOF");
		return ch;
	}
	// Read 16 bit unsigned integer from stream
	uint16_t RIFFReader::readUnsignedShort(){
		int32_t ch1=read();
		int32_t ch2=read();
		if(ch1 < 0)
			throw Exception("EOF");
		if(ch2 < 0)
			throw Exception("EOF");
		return (ch1 & 0xff) | ((ch2 & 0xff) << 8);
	}
}